package com.yss.sofa.report.engine;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.yss.sofa.framework.services.report.ReportCustomData;

//TODO: 需要极大的增强，以及更严格的字段验证，真正通过fields属性来约束record
public class DataSetData implements Serializable{
	public static class TreeNode implements Serializable {
		private static final long serialVersionUID = -195185053034478818L;
		private String node;
		private String parent;
		private int level;
		private String id;
		private String name;
		private List<TreeNode> children=new ArrayList<TreeNode>();
		public TreeNode(){}
		public TreeNode(Map<String,String> record){
			super();
			this.node=record.get("node");
			this.parent=record.get("parent");
			this.level=Integer.valueOf(record.get("level"));
			this.id=record.get("id");
			this.name=record.get("name");
		}
		/**
		 * @return the node
		 */
		public String getNode() {
		
			return node;
		}
		
		/**
		 * @param node the node to set
		 */
		public void setNode(String node) {
		
			this.node = node;
		}
		
		/**
		 * @return the parent
		 */
		public String getParent() {
		
			return parent;
		}
		
		/**
		 * @param parent the parent to set
		 */
		public void setParent(String parent) {
		
			this.parent = parent;
		}
		
		/**
		 * @return the level
		 */
		public int getLevel() {
		
			return level;
		}
		
		/**
		 * @param level the level to set
		 */
		public void setLevel(int level) {
		
			this.level = level;
		}
		
		/**
		 * @return the id
		 */
		public String getId() {
		
			return id;
		}
		
		/**
		 * @param id the id to set
		 */
		public void setId(String id) {
		
			this.id = id;
		}
		
		/**
		 * @return the name
		 */
		public String getName() {
		
			return name;
		}
		
		/**
		 * @param name the name to set
		 */
		public void setName(String name) {
		
			this.name = name;
		}
		
		/**
		 * @return the children
		 */
		public List<TreeNode> getChildren() {
		
			return children;
		}
		
		/**
		 * @param children the children to set
		 */
		public void setChildren(List<TreeNode> children) {
		
			this.children = children;
		}
		public boolean isLeaf() {
			return this.children.size()==0;
		}
	}
	public static final DataSetData toFieldData(DataSetData data,String field){
		field=field.toLowerCase();
		DataSetData result=new DataSetData();
		result.addField(field);
		for(String f:data.fields){
			Map<String, String> record=new HashMap<String, String>();
			record.put(field, f);
			result.addRecord(record);
		}
		return result;
	}
	public static final List<Map<String,String>> toFormList(DataSetData data){
		List<Map<String,String>> list=new ArrayList<Map<String,String>>();
		for(Map<String,String> record:data.records){
			Map<String,String> item=new HashMap<String, String>();
			item.put("id", record.get("id"));
			item.put("name", record.get("name"));
			list.add(item);
		}
		return list;
	}
	private static TreeNode findParent(List<TreeNode> tree,String id){
		TreeNode parent=null;
		if(tree==null || tree.size()==0){
			return parent;
		}
		int count=tree.size();
		for(int i=0;i<count;i++){
			TreeNode node=tree.get(i);
			if(node.getNode().equals(id)){
				parent=node;
				break;
			}
		}
		if(parent==null){
			for(int i=0;i<count;i++){
				TreeNode node=tree.get(i);
				parent=findParent(node.getChildren(), id);
				if(parent!=null){
					break;
				}
			}
		}
		return parent;
	}
	public static final List<TreeNode> toFormTree(DataSetData data){
		int count=data.size();
		List<TreeNode> tree=new ArrayList<TreeNode>();
		for(int i=0;i<count;i++){
			Map<String,String> record=data.records.get(i);
			TreeNode parent=findParent(tree, record.get("parent"));
			if(parent!=null){
				parent.getChildren().add(new TreeNode(record));
			} else {
				tree.add(new TreeNode(record));
			}
		}
		return tree;
	}
	private static final long serialVersionUID = 6900885212444763389L;
	public static final String FIELD_NONE=Const.NONE;
	//private String[] fields=new String[0];
	private List<String> fields=new LinkedList<String>();
	private List<Map<String, String>> records=new LinkedList<Map<String,String>>();
	public DataSetData(){}
	public DataSetData(ReportCustomData data){
		this();
		Set<String> fs=data.getFields();
		List<Map<String, String>> rs=data.getData();
		this.fields.clear();
		for(String f:fs){
			this.fields.add(f.toLowerCase());
		}
		this.records=rs;
	}
	//API===========================================================================================
	public boolean addField(CharSequence field){
		if(StringUtils.isNotEmpty(field)){
			String s=field.toString().toLowerCase();
			if(!fields.contains(s)){
				return fields.add(s);
			}
		}
		return false;
	}
	public void addFields(Set<CharSequence> fs){
		this.fields.clear();
		for(CharSequence f:fs){
			if(StringUtils.isNotEmpty(f)){
				this.fields.add(f.toString().toLowerCase());
			}
		}
	}
	public void addRecords(List<Map<? extends CharSequence,? extends CharSequence>> rs){
		this.records.clear();
		for(Map<? extends CharSequence,? extends CharSequence> _:rs){
			Map<String, String> record=new HashMap<String, String>();
			for(String field:fields){
				CharSequence value=_.get(field);
				record.put(field.toLowerCase(), (value==null)?null:value.toString());
			}
			this.records.add(record);
		}
	}
	public void addRecord(Map<? extends CharSequence,? extends CharSequence> record){
		if(record!=null){
			Map<String, String> r=new HashMap<String, String>();
			for(String field:fields){
				String key=field;
				CharSequence value=record.get(key);
				r.put(key.toString().toLowerCase(), (value==null)?null:value.toString());
			}
			records.add(r);
		}
	}
	public void insertRecord(int index,Map<? extends CharSequence,? extends CharSequence> record){
		if(record!=null){
			Map<String, String> r=new HashMap<String, String>();
			for(String field:fields){
				String key=field;
				CharSequence value=record.get(key);
				r.put(key.toString().toLowerCase(), (value==null)?null:value.toString());
			}
			records.add(index,r);
		}
	}
	public void resetRecord(int index,Map<? extends CharSequence,? extends CharSequence> record){
		if(record!=null){
			Map<String, String> r=records.get(index);
			r.clear();
			for(String field:fields){
				String key=field;
				CharSequence value=record.get(key);
				r.put(key.toString().toLowerCase(), (value==null)?null:value.toString());
			}
		}
	}
	public int getCount(){
		return records.size();
	}
	public int size(){
		return this.getCount();
	}
	public int getFieldCount(){
		return fields.size();
	}
	public List<String> getValues(CharSequence field) throws Exception{
		List<String> result;
		if(FIELD_NONE.equalsIgnoreCase(field.toString())){
			result=new ArrayList<String>();
			for(int i=0,l=records.size();i<l;i++){
				result.add("");
			}
			return result;
		}
		if(!fields.contains(field.toString().toLowerCase())){
			throw new Exception("字段["+field+"]不存在");
		}
		result=new ArrayList<String>();
		for(Map<String,String> record:records){
			result.add(record.get(field.toString().toLowerCase()));
		}
		return result;
	}
	public Map<String,String> first(){
		if(records.isEmpty()){
			return null;
		}
		return records.get(0);
	}
	public String first(CharSequence field) throws Exception{
		if(records.isEmpty()){
			return null;
		}
		if(FIELD_NONE.equalsIgnoreCase(field.toString())){
			return "";
		}
		if(!fields.contains(field.toString().toLowerCase())){
			throw new Exception("字段["+field+"]不存在");
		}
		Object v = records.get(0).get(field.toString().toLowerCase());
		return (v==null)?null:v.toString();
	}
	public Map<String,String> last(){
		if(records.isEmpty()){
			return null;
		}
		return records.get(records.size()-1);
	}
	public String last(CharSequence field) throws Exception{
		if(records.isEmpty()){
			return null;
		}
		if(FIELD_NONE.equalsIgnoreCase(field.toString())){
			return "";
		}
		if(!fields.contains(field.toString().toLowerCase())){
			throw new Exception("字段["+field+"]不存在");
		}
		Object v = records.get(records.size()-1).get(field.toString().toLowerCase());
		return (v==null)?null:v.toString();
	}
	public String getValue(int index,CharSequence field) throws Exception{
		if(FIELD_NONE.equalsIgnoreCase(field.toString())){
			return "";
		}
		if(!fields.contains(field.toString().toLowerCase())){
			throw new Exception("字段["+field+"]不存在");
		}
		Object v = records.get(index).get(field.toString().toLowerCase());
		return (v==null)?null:v.toString();
	}
	public List<String> getFields(){
		return this.fields;
	}
	public boolean hasField(CharSequence field){
		if(StringUtils.isEmpty(field)){
			return false;
		}
		return this.fields.contains(field.toString().toLowerCase());
	}
	public String getField(int index){
		return fields.get(index);
	}
	public List<Map<String,String>> getRecords(){
		return this.records;
	}
	public Map<String,String> getRecord(int index){
		return records.get(index);
	}
	public void reverse(){
		Collections.reverse(records);
	}
	public void reverseField(){
		Collections.reverse(fields);
	}
	public double sum(CharSequence field){
		if(!fields.contains(field.toString().toLowerCase())){
			return 0;
		}
		double result=0;
		for(Map<String,String> record:records){
			String value=record.get(field.toString().toLowerCase());
			double d=0;
			try {
				d=Double.valueOf(value);
			} catch (Exception e) {
			}
			result+=d;
		}
		return result;
	}
	public double avg(CharSequence field){
		if(!fields.contains(field.toString().toLowerCase())){
			return 0;
		}
		if(records.size()==0){
			return 0;
		}
		double result=0;
		for(Map<String,String> record:records){
			String value=record.get(field.toString().toLowerCase());
			double d=0;
			try {
				d=Double.valueOf(value);
			} catch (Exception e) {
			}
			result+=d;
		}
		return result/records.size();
	}
	public static DataSetData none(){
		return new DataSetData(){
			private static final long serialVersionUID = -8935031166475349490L;
			public int getCount(){
				return 0;
			}
			public int size(){
				return this.getCount();
			}
			public int getFieldCount(){
				return 0;
			}
			public String getValue(int index,CharSequence field){
				return null;
			}
			public String getField(int index){
				return null;
			}
			public Map<String,String> getRecord(int index){
				throw new IndexOutOfBoundsException("Index: "+index+", Size: 0");
			}
		};
	}
	public int countOf(CharSequence field,boolean ignoreEmpty){
		if(!fields.contains(field.toString().toLowerCase())){
			return 0;
		}
		if(records.size()==0){
			return 0;
		}
		Set<String> values=new HashSet<String>();
		for(Map<String,String> record:records){
			String value=record.get(field.toString().toLowerCase());
			if(ignoreEmpty && StringUtils.isEmpty(value)){
				continue;
			}
			values.add(value);
		}
		return values.size();
	}
	public void print(){
		if(records.size()==0){
			System.out.println("<data is empty>");
		}
		System.out.print("-------------------------------------------------------------\n");
		for(String key:fields){
			System.out.print(key+"\t");
		}
		System.out.print("\n-------------------------------------------------------------\n");
		for(Map<String,String> record:records){
			for(String key:fields){
				String value=record.get(key);
				System.out.print((value==null?"null":value.replaceAll("\t", " ").replaceAll("\n", " ").replaceAll("\r", ""))+"\t");
			}
			System.out.println();
		}
		System.out.print("-------------------------------------------------------------\n");
	}
	//API end=======================================================================================
	public DataSetData convert(){
		List<String> fs=new ArrayList<String>(this.fields.size());
		fs.addAll(this.fields);
		this.fields=fs;
		List<Map<String,String>> rs=new ArrayList<Map<String,String>>(this.records.size());
		rs.addAll(this.records);
		this.records=rs;
		return this;
	}
}
